package com.android.controller;

import java.lang.Math;

/**
 * Random Number Generator Class - Random Number Generator that creates 
 * uniformly distributed random numbers or exponentially distributed random numbers. 
 * 
 * @author David Alban <dalban@ncsu.edu>
 * @version 1.0
 */
public class RandomNumberGenerator
{
	/**
	 * Mersenne prime number, M31 = 2^31 - 1 = 2147483647
	 */		
	protected static final long M = 2147483647;	
	/**
	 * Primitive root modulo of M, 7^5 = 16807
	 */		
	protected static final long A = 16807;
	/**
	 * 127773
	 */		
	protected static final long Q = 127773;
	/**
	 * 2836
	 */		
	protected static final long R = 2836;
	/**
	 * Mask for unlikely value
	 */		
	protected static final long MASK = 123459876;
	/**
	 * Reciprocal of Mersenne prime number, 1/M31 = 1/2147483647
	 */		
	protected static final double RM = 1.0/2147483647;	
	/**
	 * Seed
	 */		
	protected long seed;
	/**
	 * Park-Miller random number. 
	 */		
	protected double rand0;
	/**
	 * Exponentially distributed random number.
	 */		
	protected double expDev;
	
	/**
	 * Constructor - Creates the random number generator.
	 */
	public RandomNumberGenerator()
	{
		seed = 0;
	}

	/**
	 * Constructor - Creates the random number generator with a given seed.
	 * @param Seed		Random number generator seed
	 */
	public RandomNumberGenerator(long Seed)
	{
		seed = Seed;
	}
	
	/**
	 * Modifier Method - Sets the random number generator seed.
	 * @param Seed		Random number generator seed
	 */
	public void setSeed(long Seed)
	{
		seed = Seed;
	}
	
	/**
	 * Modifier Method - Sets the Park-Miller random number, which is uniformly
	 * distributed in (0,1).
	 * @param Seed		Random number generator seed
	 */
	public void setRand0(long Seed)
	{
		long k;

		seed = Seed;
		seed ^= MASK;	// Allows zero to be used as seed
		
		k = seed/Q;
		seed = A *(seed-k*Q)- R*k;

		if(seed < 0)
		{
			seed += M;
		}
		
		rand0 = RM * seed;
	}
	
	/**
	 * Modifier Method - Sets the exponentially distributed random number.
	 * @param Seed		Random number generator seed
	 */
	public void setExpDev(long Seed)
	{
		double dummy;
		
		seed = Seed;
		
		do
		{
			setRand0(seed);
			dummy = rand0;
		}while(dummy == 0.0);
		
		expDev = -Math.log10(dummy);
	}
	
	/**
	 * Accessor Method - Returns the random number generator seed.
	 * @return seed		Random number generator seed
	 */	
	public long getSeed()
	{
		return seed;
	}
	
	/**
	 * Accessor Method - Returns the Park-Miller random number, which is 
	 * uniformly distributed in (0,1).
	 * @return rand0	Park-Miller random number
	 */	
	public double getRand0()
	{
		return rand0;
	}
	
	/**
	 * Accessor Method - Returns exponentially distributed random number.
	 * @return expDev	Exponentially distributed random number
	 */	
	public double getExpDev()
	{
		return expDev;
	}
	
	/**
	 * This method provides unit testing of the methods of this class.
	 * @param args		Command line arguments
	 */
	public static void main (String []args)
	{
		RandomNumberGenerator r = new RandomNumberGenerator();
		
		System.out.println("----------------------------------------");
		System.out.println("  Random Number uniformly distributed   ");
		System.out.println("----------------------------------------");
		
		r.setRand0(0);
		System.out.println("seed = " + r.getSeed());
		System.out.println(r.getRand0());
		System.out.println();
		
		r.setRand0(1);
		System.out.println("seed = " + r.getSeed());
		System.out.println(r.getRand0());
		System.out.println();
		
		r.setSeed(0);
		
		for(int i=0; i<100; i++)
		{
			r.setRand0(r.seed);
			System.out.println("seed " + i + " = " + r.getSeed());
			System.out.println(r.getRand0());
			System.out.println();
		}
		
		System.out.println("----------------------------------------");
		System.out.println("Random Number exponentially distributed ");
		System.out.println("----------------------------------------");
		
		r.setSeed(0);
		
		for(int i=0; i<100; i++)
		{
			r.setExpDev(r.seed);
			System.out.println();
			System.out.println("seed " + i + " = " + r.getSeed());
			System.out.println(r.getExpDev());
		}
		
		r.setSeed(100);
		
		for(int i=0; i<100; i++)
		{
			r.setExpDev(r.seed);
			System.out.println();
			System.out.println("seed " + i + " = " + r.getSeed());
			System.out.println(r.getExpDev());
		}
	}
}
